package com.moduleSys.shiro;

import com.moduleSys.entity.system.Menu;
import com.moduleSys.entity.system.Role;
import com.moduleSys.entity.system.User;
import com.moduleSys.mapper.MenuMapper;
import com.moduleSys.mapper.RoleMapper;
import com.moduleSys.mapper.UserMapper;
import com.moduleSys.utils.MD5;
import org.apache.shiro.SecurityUtils;
import org.apache.shiro.authc.*;
import org.apache.shiro.authz.AuthorizationInfo;
import org.apache.shiro.authz.SimpleAuthorizationInfo;
import org.apache.shiro.crypto.hash.Md5Hash;
import org.apache.shiro.realm.AuthorizingRealm;
import org.apache.shiro.session.Session;
import org.apache.shiro.subject.PrincipalCollection;
import org.apache.shiro.subject.SimplePrincipalCollection;
import org.apache.shiro.subject.Subject;
import org.apache.shiro.util.ByteSource;
import org.junit.Test;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.data.redis.core.StringRedisTemplate;
import org.springframework.data.redis.core.ValueOperations;
import org.springframework.util.StringUtils;

import javax.annotation.Resource;
import java.util.*;
import java.util.concurrent.TimeUnit;

/**
 * Created by yangqj on 2017/4/21.
 */
public class MyShiroRealm extends AuthorizingRealm {

    private Logger logger = LoggerFactory.getLogger(MyShiroRealm.class);

    @Resource
    private UserMapper userMapper;

    @Resource
    private MenuMapper menuMapper;

    @Autowired
    private RoleMapper roleMapper;

    private static final String STATUS = "1";

    @Autowired
    private StringRedisTemplate stringRedisTemplate;

    /*@Autowired
    private RoleMapper roleMapper;*/

    //用户登录次数计数  redisKey 前缀

    private String SHIRO_LOGIN_COUNT = "shiro_login_count_";

    //用户登录是否被锁定    一小时 redisKey 前缀

    private String SHIRO_IS_LOCK = "shiro_is_lock_";

    private static final int COUNT_MAX_NUMB = 5;

    /**
     * 认证信息.(身份验证) : Authentication 是用来验证用户身份
     * @param authcToken
     * @return
     * @throws AuthenticationException
     */
    @Override
    protected AuthenticationInfo doGetAuthenticationInfo(
            AuthenticationToken authcToken) throws AuthenticationException {
        UsernamePasswordToken token = (UsernamePasswordToken) authcToken;
        String name = token.getUsername();
        String password = String.valueOf(token.getPassword());

        // 从数据库获取对应用户名密码的用户
        User user = userMapper.selectUserByUserName(name);
        //访问一次，计数一次
        ValueOperations<String, String> opsForValue = stringRedisTemplate.opsForValue();
        opsForValue.increment(SHIRO_LOGIN_COUNT+password, 1);
        //计数大于5时，设置用户被锁定12小时
        if(Integer.parseInt(opsForValue.get(SHIRO_LOGIN_COUNT+password)) >= MyShiroRealm.COUNT_MAX_NUMB){
            opsForValue.set(SHIRO_IS_LOCK+password, "LOCK");
            stringRedisTemplate.expire(SHIRO_IS_LOCK+password, 12, TimeUnit.HOURS);
            if( user != null ){
                user.setLastLoginTime(new Date());
                user.setStatus(1);
                userMapper.updateUserById(user);
            }
            throw new LockedAccountException("由于密码输入错误次数大于5次，帐号已被锁定12小时！");
        }
        String passwordMd5Hash;
        try {
            passwordMd5Hash = new Md5Hash(password, user.getSalt(),6).toHex();
        }catch (Exception uae){
            throw new UnknownAccountException();
        }
        if ( user == null ){
            throw new UnknownAccountException();
        }else if( !passwordMd5Hash.equals(user.getPassword()) ){
            throw new IncorrectCredentialsException();
        }else{
            if(MyShiroRealm.STATUS.equals(user.getStatus())){
                /**
                 * 如果用户的status为禁用。那么就抛出<code>DisabledAccountException</code>
                 */
                throw new LockedAccountException("由于密码输入错误次数大于5次，帐号已被锁定12小时！");
            }else{
                //登录成功
                //更新登录时间 last login time
                user.setLastLoginTime(new Date());
                user.setStatus(0);
                userMapper.updateUserById(user);
                //清空登录计数
                opsForValue.set(SHIRO_LOGIN_COUNT+password, "0");
            }
            Subject subject = SecurityUtils.getSubject();
            Session session = subject.getSession();
            session.setAttribute("user", user);
            logger.info("身份认证成功，登录用户："+name);
            logger.info("sessionId -------> "+session.getId());
            ByteSource credentialsSalt1 = ByteSource.Util.bytes(user.getSalt());
            SimpleAuthenticationInfo info1 = new SimpleAuthenticationInfo(user, passwordMd5Hash, credentialsSalt1, this.getName());
            return info1;
        }
    }



    //授权
    @Override
    protected AuthorizationInfo doGetAuthorizationInfo(PrincipalCollection principalCollection) {
        User user= (User) SecurityUtils.getSubject().getPrincipal();//User{id=1, username='admin', password='3ef7164d1f6167cb9f2658c07d3c2f0a', enable=1}
        List<Role> roleList = roleMapper.selectByUserId(user.getId());
        // 权限信息对象info,用来存放查出的用户的所有的角色（role）及权限（permission）
        SimpleAuthorizationInfo info = new SimpleAuthorizationInfo();
        Set<String> roleSet = new LinkedHashSet<>();
        Set<String> permissionSet = new LinkedHashSet<>();
        List<Long> roleIds = new ArrayList<>();
        for(Role role: roleList){
            roleSet.add(role.getRoleName());
            roleIds.add(role.getId());
        }
        List<Menu> menuList = menuMapper.getMenuByRoleIds(roleIds);
        if( menuList != null && menuList.size() > 0 ){
            menuList.forEach(menu ->{
                if( !StringUtils.isEmpty(menu.getPermission()) ){
                    permissionSet.add(menu.getPermission());
                }
            });
        }
        info.setRoles(roleSet);
        info.setStringPermissions(permissionSet);
        return info;
    }


    /**
     * 指定principalCollection 清除
     */
//    public void clearCachedAuthorizationInfo(PrincipalCollection principalCollection) {
//
//        SimplePrincipalCollection principals = new SimplePrincipalCollection(
//                principalCollection, getName());
//        super.clearCachedAuthorizationInfo(principals);
//    }

    @Test
    public void test(){
/*        String s = StringUtils.randomString(20);
        //rwQsYQUJ2bYvN3D6EG1g
        System.out.println(s);*/
      MD5 md5 = new MD5();
        String md5ofStr = md5.getMD5ofStr("123");
        /**
         * 202cb962ac59075b964b07152d234b70
         */
        System.out.println(md5ofStr);
        String passwordMd5Hash = new Md5Hash(
                "202cb962ac59075b964b07152d234b70",
                "rwQsYQUJ2bYvN3D6EG1g",
                6).toHex();
        /**
         * e07a1c30e80c3c5fb2fc1ac096df4b68
         */
        System.out.println(passwordMd5Hash);
    }

}
